import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashMap;
import java.util.Map.Entry;

public class SpellChecker implements ISpellChecker {

    private String[] dictionary;

    public SpellChecker() {
        dictionary = DictionaryLoader.loadDictionary();
    }

    @Override
    public SpellCheckResult check(String word) {
        long time = Calendar.getInstance().getTimeInMillis();
        word = word.toLowerCase(); 
        SpellCheckResult result = binarySearch(word);

        long endTime = Calendar.getInstance().getTimeInMillis();
        System.out.println("It took " + (endTime - time)
                + " miliseconds to complete the search");
        return result;
    }

    public SpellCheckResult binarySearch(String word) {
        int index = Arrays.binarySearch(dictionary, word);
        if (index >= 0) {
            return new SpellCheckResult(true, null, null);
        } else {
            int beforeIndex = - index - 2, 
                    afterIndex = - index;
            String before = beforeIndex >= 0 && beforeIndex < dictionary.length 
                    ? dictionary[beforeIndex] : null;
            String after = afterIndex >= 0 && afterIndex < dictionary.length
                    ? dictionary[afterIndex] : null;
            return new SpellCheckResult(false, before, after);
        }
    }

    // the method goes through the whole dictionary seraching for the
    // word and if it doesn't find it
    // it creates a new dictionary with the word in it
    // and checks where the word is in this time
    public SpellCheckResult forLoopSearch(String word) {
        SpellCheckResult result;
        for (int i = 0; i < dictionary.length; i++) {
            if (word.equals(dictionary[i]))
                return new SpellCheckResult(true, null, null);
        }
        ArrayList<String> listDictionary = new ArrayList<String>(
                Arrays.asList(dictionary));
        listDictionary.add(word);
        String[] newDictionary = listDictionary
                .toArray(new String[listDictionary.size()]);
        Arrays.sort(newDictionary);
        int newIndex = Arrays.binarySearch(newDictionary, word);
        String before = (newIndex - 1 >= 0 && newIndex - 1 < newDictionary.length) ? newDictionary[newIndex - 1]
                : null;
        String after = (newIndex + 1 >= 0 && newIndex + 1 < newDictionary.length) ? newDictionary[newIndex + 1]
                : null;
        result = new SpellCheckResult(false, before, after);
        
        return result;
    }
    

    
    // this method tries to find the words that have most
    // letters in common with the givn word
    // looking only in words that have the same number
    // of symbols, one less or one more
    public SpellCheckResult searchMostCommonResult(String word) {
        int length = word.length();
        HashMap<String, Integer> commonLetters = new HashMap<String, Integer>();
        for (int i = 0; i < dictionary.length; i++) {
            String dictonaryWord = dictionary[i];
            if (dictonaryWord.length() <= length + 1 
                    && dictonaryWord.length() >= length - 1) {
                int lettersInCommon = 0;
                for (int k = 0; k < length; k++) {
                    for (int j = k; j < dictonaryWord.length(); j++) {
                        if (dictonaryWord.charAt(j) == word.charAt(k)) {
                            lettersInCommon++;
                            break;
                        }
                    }
                }
                commonLetters.put(dictonaryWord, lettersInCommon);
            }
        }
        SpellCheckResult result;
        int max1 = Integer.MIN_VALUE, max2 = Integer.MIN_VALUE;
        String bestGuess1 = null, bestGuess2 = null;
        for (Entry<String, Integer> entry : commonLetters.entrySet()) {
            if (entry.getValue() > max1) {
                max2 = max1;
                max1 = entry.getValue();
                bestGuess2 = bestGuess1;
                bestGuess1 = entry.getKey();
            } else if (entry.getValue() > max2){
                max2 = entry.getValue();
                bestGuess2 = entry.getKey();
            }
        }
        
        result = new SpellCheckResult(false, bestGuess1, bestGuess2);
        return result;
    }

}
